package roborally.model;

import be.kuleuven.cs.som.annotate.*;

/**
 * A class for dealing with items involving a weight.
 * 
 * @invar	The weight of each item must be a legal weight for an item.
 * 			| canHaveAsWeight(weight)
 * @invar	An item can't both be owned by a robot and be located on a board at the same time.
 * 			| if( isInUse() )
 * 			|	then getBoard() == null
 * 
 * @version  1.0
 * @author  Pieter Robberechts	(1e Bach Informatica)
 * @author	Jeroen Sanders		(1e Bach Informatica)
 *  
 */
public abstract class Item extends Element implements Comparable<Item> {

	/**
	 * Initialize this new item with given weight.
	 * 
	 * @param 	weight
	 * 			The weight for the item (in grams).
	 * @post	...
	 * 			| if(canHaveAsWeight(weight))
	 * 			| 	then this.weight.isIdenticalTo(weight)
	 * @post	...
	 * 			| if(!canHaveAsWeight(weight))
	 * 			|	then this.weight.isIdnticalTo(getStandardWeight())
	 */
	public Item(Weight weight){
		if(canHaveAsWeight(weight))
			this.weight = weight;
		else
			this.weight = getStandardWeight();
	}
	
	/**
	 * Check whether the given weight is a valid weight for an item.
	 * 
	 * @param 	weight
	 *         	The weight to check.
	 * @return 	...
	 *       	| result == ( weight != null && weight.getWeightInUnits() > 0 )
	 */
	@Raw
	public boolean canHaveAsWeight(Weight weight){
		return weight != null && weight.getWeightInUnits() > 0;
	}
	
	/**
	 * Return the weight of this item.
	 */
	@Basic @Raw @Immutable
	public Weight getWeight(){
		return weight;
	}
	
	/**
	 * Return a standard weight for an item, depending on the type of this item.
	 */
	@Basic
	public abstract Weight getStandardWeight();
	
	/**
	 * Variable registering the weight of this board.
	 */
	private final Weight weight;
	
	/**
	 * Make a given robot use an item.
	 * 
	 * @param 	robot
	 * 			The robot which should use a specified item.
	 */
	public abstract void use(Robot robot);
	
	/**
	 * Change the state of this item to 'in use' or 'not in use'.
	 * 
	 * @post	...
	 * 			| if state == true
	 * 			|	then new.isInUse() == true
	 * 			| if state == false
	 * 			|	then new.isInUse() == false
	 */
	@Basic
	public void setInUse(boolean state) {
		inUse = state;
	}
	
	/**
	 * Check whether this item is part of the inventory of a robot.
	 */
	@Basic
	public boolean isInUse() {
		return inUse;
	}
	
	/**
	 * Variable registering whether or not this item is part of the inventory of a robot.
	 */
	private boolean inUse = false;
	
	/**
	 * Terminate this item, if possible.
	 *
	 * @effect 	...
	 * 			| if( ! isInUse() )
	 *       	| 	then super.terminate() 
	 * @throws	IllegalStateException
	 * 			The item may not be part of the inventory of a robot.
	 * 			| ! isInUse()      
	 */
	@Override
	public void terminate() throws IllegalStateException {
		if(isInUse())
			throw new IllegalStateException("You can't terminate an item which is part of the inventory of a robot.");
		super.terminate();
	}
	
	/**
	 * Check whether this item can share its position with a second element, which has a higher position in the hierarchy.
	 * 
	 * @param 	other
	 * 			The element to check for whether it can share its position with the prime element.
	 * @return	...
	 * 			| result == ( other instanceof Item || other instanceof Robot )
	 */
	@Override
	public boolean canSharePositionWithCore(Element other){
		return other instanceof Item || other instanceof Robot;
	}
	
	/**
	 * Compare an item with a given second item based on their weight.
	 * 
	 * @param	other
	 * 			The item to compare the prime item with.
	 * @return	...
	 * 			| if(this.getWeight().lessThan(other.getWeight()))
	 * 			| 	then result == -1
	 * 			| if(this.getWeight().lessThan(other.getWeight()))
	 * 			| 	then result == 1
	 * 			| if(this.getWeight().isIdenticalTo(other.getWeight()))	
	 * 			|	then result == 0
	 * @throws 	IllegalArgumentException
	 * 			The given item is ineffective.
	 * 			| other == null
	 */
	public int compareTo(Item other){
		if(other == null)
			throw new IllegalArgumentException("The given item is ineffective.");
		if(this.getWeight().lessThan(other.getWeight()))
			return -1;
		else if(this.getWeight().moreThan(other.getWeight()))
			return 1;
		else
			return 0;
   }
	
}
